package com.ds.esd.util;

import com.alibaba.fastjson.util.TypeUtils;
import com.ds.common.JDSConstants;
import com.ds.common.logging.Log;
import com.ds.common.logging.LogFactory;
import com.ds.common.util.ClassUtility;
import com.ds.config.TreeListResultModel;
import com.ds.context.JDSActionContext;
import com.ds.context.JDSContext;
import com.ds.context.MinServerActionContextImpl;
import com.ds.esd.dsm.aggregation.AggEntityConfig;
import com.ds.esd.dsm.view.ViewEntityConfig;
import com.ds.esd.custom.ESDClass;
import com.ds.esd.custom.ESDField;
import com.ds.esd.custom.bean.CustomViewBean;
import com.ds.esd.custom.bean.MethodConfig;
import com.ds.esd.custom.bean.nav.foldingtree.NavFoldingTreeViewBean;
import com.ds.esd.custom.bean.nav.tree.NavTreeViewBean;
import com.ds.esd.custom.enums.CustomMenuItem;
import com.ds.esd.custom.form.pop.PopTreeViewBean;
import com.ds.esd.custom.tree.ChildTreeViewBean;
import com.ds.esd.custom.tree.CustomTreeViewBean;
import com.ds.esd.custom.tree.enums.TreeItem;
import com.ds.esd.dsm.DSMFactory;
import com.ds.esd.tool.ui.component.list.TreeListItem;
import com.ds.jds.core.esb.EsbUtil;
import com.ds.jds.core.esb.util.OgnlUtil;
import com.ds.server.httpproxy.core.HttpRequest;
import com.ds.web.ConstructorBean;
import com.ds.web.RequestMethodBean;
import com.ds.web.RequestParamBean;
import ognl.OgnlContext;
import ognl.OgnlException;
import ognl.OgnlRuntime;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.Callable;

public class FillItemTask<T extends TreeListItem> implements Callable<T> {
    private static final Log logger = LogFactory.getLog(JDSConstants.CONFIG_KEY, FillItemTask.class);

    private MinServerActionContextImpl autoruncontext;
    private Object obj;
    private Class<T> clazz;
    private List<String> ids = new ArrayList<>();
    private OgnlContext onglContext;
    private CustomViewBean customViewBean;

    public FillItemTask(Object object, Class<T> clazz, CustomViewBean customViewBean, List<String> ids) {
        this.clazz = clazz;
        this.obj = object;
        this.ids = ids;
        this.customViewBean = customViewBean;
        JDSContext context = JDSActionContext.getActionContext();
        this.autoruncontext = new MinServerActionContextImpl(context.getHttpRequest());
        autoruncontext.setParamMap(context.getContext());
        if (context.getSessionId() != null) {
            autoruncontext.setSessionId(context.getSessionId());
            autoruncontext.getSession().put("sessionHandle", context.getSession().get("sessionHandle"));
        }
        autoruncontext.setSessionMap(context.getSession());
        HttpRequest request = (HttpRequest) JDSActionContext.getActionContext().getHttpRequest();
        onglContext = request.getOgnlContext();

    }

    private ChildTreeViewBean getChildTreeViewBean(CustomTreeViewBean viewBean, Class sourceClazz, Object obj) {
        ChildTreeViewBean childTreeViewBean = null;
        Class treeClass = obj.getClass();

        if (!viewBean.getEntityClassName().equals(sourceClazz.getName())) {
            viewBean = new CustomTreeViewBean(sourceClazz, viewBean);
        }

        if (obj instanceof TreeItem) {
            return viewBean.getChildTreeBean((TreeItem) obj);
        }

        Set<ChildTreeViewBean> childTreeViewBeans = viewBean.getChildTreeViewBeans();
        for (ChildTreeViewBean child : childTreeViewBeans) {
            try {
                Constructor constructor = child.getConstructorBean().getSourceConstructor();
                Class[] paramClass = constructor.getParameterTypes();
                if (paramClass.length > 0 && paramClass[0].isAssignableFrom(treeClass) && constructor.getDeclaringClass().equals(sourceClazz)) {
                    return child;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        Constructor[] constructors = sourceClazz.getDeclaredConstructors();
        for (Constructor constructor : constructors) {
            Class[] paramClass = constructor.getParameterTypes();
            if (paramClass.length > 0 && paramClass[0].isAssignableFrom(treeClass)) {
                try {
                    childTreeViewBean = new ChildTreeViewBean(constructor, viewBean);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }


        return childTreeViewBean;
    }

    @Override
    public T call() throws Exception {
        JDSActionContext.setContext(autoruncontext);
        CustomTreeViewBean viewBean = null;
        if (customViewBean instanceof NavTreeViewBean) {
            viewBean = ((NavTreeViewBean) customViewBean).getTreeViewBean();
        } else if (customViewBean instanceof NavFoldingTreeViewBean) {
            NavFoldingTreeViewBean navTreeViewBean = (NavFoldingTreeViewBean) customViewBean;
            viewBean = navTreeViewBean.getTreeViewBean();
        } else if (customViewBean instanceof PopTreeViewBean) {
            PopTreeViewBean navTreeViewBean = (PopTreeViewBean) customViewBean;
            viewBean = navTreeViewBean.getTreeViewBean();
        } else {
            viewBean = (CustomTreeViewBean) customViewBean;
        }
        AggEntityConfig aggConfig = DSMFactory.getInstance().getAggregationManager().getAggEntityConfig(obj.getClass().getName(), viewBean.getDomainId());
        ChildTreeViewBean childTreeViewBean = getChildTreeViewBean(viewBean, clazz, obj);
        ConstructorBean constructorBean = childTreeViewBean.getConstructorBean();
        List<Object> objectSet = new ArrayList<>();
        Set<RequestParamBean> requestParamBeans = constructorBean.getParamSet();
        for (RequestParamBean paramBean : requestParamBeans) {
            if (obj.getClass().equals(paramBean.getParamClass()) || paramBean.getParamClass().isAssignableFrom(obj.getClass())) {
                objectSet.add(obj);
            } else {
                Object value = TypeUtils.cast(JDSActionContext.getActionContext().getParams(paramBean.getParamName()), paramBean.getParamClass(), null);
                objectSet.add(value);
            }
        }
        T t = (T) constructorBean.getSourceConstructor().newInstance(objectSet.toArray());
        if (viewBean != null && viewBean.getEntityClassName().equals(clazz.getName())) {
            List<String> hiddenList = viewBean.getHiddenFieldNames();
            for (String hiddenName : hiddenList) {
                try {
                    t.addTagVar(hiddenName, OgnlUtil.getValue(hiddenName, onglContext, t));
                } catch (OgnlException e) {
                    logger.warn(viewBean.getEntityClassName() + " not from field: " + hiddenName);
                }
            }
            if (t.getCaption() == null || t.getCaption().equals("")) {
                if (viewBean.getCaptionField() != null) {
                    t.setCaption(OgnlUtil.getValue(viewBean.getCaptionField().getFieldname(), onglContext, t).toString());
                } else if (aggConfig.getESDClass().getCaptionField() != null) {
                    t.setCaption(OgnlUtil.getValue(aggConfig.getESDClass().getCaptionField().getName(), onglContext, obj).toString());
                }
            }

            if (t.getId() == null || t.getId().equals("")) {
                if (viewBean.getUidField() != null) {
                    Object pkValue = OgnlUtil.getValue(viewBean.getUidField().getFieldname(), onglContext, t);
                    if (pkValue != null) {
                        t.setId(pkValue.toString());
                    }
                } else if (aggConfig.getESDClass().getUid() != null) {
                    Object pkValue = OgnlUtil.getValue(aggConfig.getESDClass().getUid(), onglContext, obj);
                    if (pkValue != null) {
                        t.setId(pkValue.toString());
                    }
                }
            }
        } else {
            ESDClass esdClass = DSMFactory.getInstance().getClassManager().getAggEntityByName(clazz.getName(), viewBean.getDomainId(), false);
            List<ESDField> hiddenList = esdClass.getHiddenFieldList();
            for (ESDField field : hiddenList) {
                try {
                    if (viewBean.getFieldNames().contains(field.getName())) {
                        t.addTagVar(field.getName(), OgnlUtil.getValue(field.getName(), onglContext, t));
                    }
                } catch (OgnlException e) {
                    logger.warn(viewBean.getEntityClassName() + " not from field: " + field.getName());
                }
            }
            if (t.getCaption() == null || t.getCaption().equals("")) {
                if (esdClass.getCaptionField() != null) {
                    t.setCaption(OgnlUtil.getValue(esdClass.getCaptionField().getName(), onglContext, t).toString());
                } else if (aggConfig.getESDClass().getCaptionField() != null) {
                    t.setCaption(OgnlUtil.getValue(aggConfig.getESDClass().getCaptionField().getName(), onglContext, obj).toString());
                }
            }

            if (t.getId() == null || t.getId().equals("")) {
                if (esdClass.getUid() != null) {
                    Object pkValue = OgnlUtil.getValue(esdClass.getUid(), onglContext, t);
                    if (pkValue != null) {
                        t.setId(pkValue.toString());
                    }
                } else if (aggConfig.getESDClass().getUid() != null) {
                    Object pkValue = OgnlUtil.getValue(aggConfig.getESDClass().getUid(), onglContext, obj);
                    if (pkValue != null) {
                        t.setId(pkValue.toString());
                    }
                }
            }

        }


        if (childTreeViewBean != null) {
            t.setDynDestory(childTreeViewBean.getDynDestory());
            t.setDynLoad(childTreeViewBean.getLazyLoad());
            t.setIniFold(childTreeViewBean.getIniFold());
            t.setCloseBtn(childTreeViewBean.getCloseBtn());
            if (t.getImageClass() == null || t.getImageClass().equals("")) {
                t.setImageClass(childTreeViewBean.getImageClass());
            }

            String bindClassName = t.getBingClassName();
            if (childTreeViewBean.getBindService() != null && !childTreeViewBean.getBindService().equals(Void.class)) {
                bindClassName = childTreeViewBean.getBindService().getName();
            }
            if (bindClassName != null && !bindClassName.equals("")) {
                ViewEntityConfig entityConfig = DSMFactory.getInstance().getViewManager().getViewEntityConfig(bindClassName, customViewBean.getDomainId(), customViewBean.getViewInstId());
                MethodConfig editorMethod = entityConfig.getCurrConfig().getMethodByItem(CustomMenuItem.treeNodeEditor);
                MethodConfig parentMethod = entityConfig.getCurrConfig().getMethodByItem(CustomMenuItem.treeNodeGrid);
                MethodConfig loadChildMethod = entityConfig.getCurrConfig().getMethodByItem(CustomMenuItem.loadChild);
                t.setGroupName(childTreeViewBean.getGroupName());
                if (editorMethod != null) {
                    t.setClassName(editorMethod.getEUClassName());
                }

                if (parentMethod != null) {
                    t.setParentClassName(parentMethod.getEUClassName());
                }

                if (loadChildMethod != null) {
                    if (!loadChildMethod.getImageClass().equals(MethodConfig.DefaultImageClass)) {
                        t.setImageClass(loadChildMethod.getImageClass());
                    }

                    Map tagVar = t.getTagVar();
                    if (tagVar == null) {
                        tagVar = new HashMap();
                    }

                    if (!tagVar.isEmpty()) {
                        JDSActionContext.getActionContext().getContext().putAll(tagVar);
                    }

                    Object object = invokMethod(loadChildMethod.getRequestMethodBean(), tagVar);
                    if (object instanceof TreeListResultModel) {
                        TreeListResultModel<Collection<TreeListItem>> childList = (TreeListResultModel) object;
                        if (childList.getData() != null && !childList.getData().isEmpty()) {
                            Collection<TreeListItem> items = childList.getData();
                            boolean canLazy = true;
                            for (TreeListItem treeListItem : items) {
                                if (treeListItem.getParentClassName() != null && !treeListItem.getParentClassName().equals("")) {
                                    t.setClassName(treeListItem.getParentClassName());
                                }
                                if (this.ids.contains(treeListItem.getId())) {
                                    canLazy = false;
                                    t.setIniFold(false);
                                }
                            }

                            if (childTreeViewBean.getLazyLoad() && viewBean.getLazyLoad() && canLazy) {
                                t.setSub(new ArrayList());
                            } else {
                                t.setSub((List) childList.getData());
                            }
                        } else if (editorMethod == null) {
                            t.setHidden(true);
                        }
                    }
                }
            }
        }

        return t;
    }


    private Object getService(RequestMethodBean methodBean, Map<String, Object> allParamsMap) throws ClassNotFoundException, OgnlException {
        Class clazz = ClassUtility.loadClass(methodBean.getClassName());
        Object service = null;
        if (clazz.getInterfaces().length > 0) {
            service = EsbUtil.parExpression(clazz.getInterfaces()[0]);
        } else {
            service = EsbUtil.parExpression(clazz);
        }

        if (service == null) {
            service = OgnlRuntime.callConstructor(onglContext, clazz.getName(), new Object[]{});
        }

        for (Field field : clazz.getDeclaredFields()) {
            if (allParamsMap.get(field.getName()) != null) {
                try {
                    OgnlRuntime.setProperty(onglContext, service, field.getName(), TypeUtils.cast(allParamsMap.get(field.getName()), field.getType(), null));
                } catch (OgnlException e) {
                }
            }
        }
        return service;
    }


    public Object invokMethod(RequestMethodBean methodBean, Map<String, Object> allParamsMap) throws ClassNotFoundException, OgnlException {
        Object object = null;
        Object service = getService(methodBean, allParamsMap);
        Map<String, String> paramsMap = methodBean.getParamsMap();
        Set<RequestParamBean> keySet = methodBean.getParamSet();
        Object[] objects = new Object[paramsMap.size()];
        Class[] objectTyps = new Class[paramsMap.size()];
        int k = 0;
        for (RequestParamBean paramBean : keySet) {
            String key = paramBean.getParamName();
            Class ctClass = ClassUtility.loadClass(paramsMap.get(paramBean.getParamName()));
            String iClassName = ctClass.getName();
            Class iClass = ClassUtility.loadClass(iClassName);
            Object value = null;
            if (allParamsMap.get(key) != null) {
                value = TypeUtils.cast(allParamsMap.get(key), iClass, null);
            }
            objectTyps[k] = iClass;
            objects[k] = value;
            k = k + 1;
        }
        if (service != null) {
            object = OgnlRuntime.callMethod(onglContext, service, methodBean.getMethodName(), objects);
        }
        return object;
    }
}